#include "drivers/ext/disk_fatfs.h"
#include "fs_init_fatfs.h"
#include "sys_log.h"

#include <unistd.h>
#include <fcntl.h>

#define IMG_DISK_SECTION_SIZE (512)
#define IMG_DISK_SECTION_COUNT (1024)

#define IMG_DISK_FILE "/dev/shm/fatfs.img"

static DSTATUS _fatfs_disk_status(void);
static DSTATUS _fatfs_disk_initialize(void);
static DRESULT _fatfs_disk_read(BYTE *buff, LBA_t sector, UINT count);
static DRESULT _fatfs_disk_write(const BYTE *buff, LBA_t sector, UINT count);
static DRESULT _fatfs_disk_ioctl(BYTE cmd, void *buff);

static device_disk_fatfs_data_t s_data;

static struct device_disk_fatfs const s_device = {
    .api = {
        .disk_status = _fatfs_disk_status,
        .disk_initialize = _fatfs_disk_initialize,
        .disk_read = _fatfs_disk_read,
        .disk_write = _fatfs_disk_write,
        .disk_ioctl = _fatfs_disk_ioctl,
    },
    .data = &s_data,
};
OBJ_EXPORT_DEVICE(s_device, "disk:/ff:img");

static DSTATUS _fatfs_disk_status(void)
{
    return RES_OK;
}

static DSTATUS _fatfs_disk_initialize(void)
{
    DRESULT ret = RES_OK;
    if (access(IMG_DISK_FILE, F_OK) != 0)
    {
        int mmc_fd = open(IMG_DISK_FILE, O_CREAT | O_RDWR, 0777);
        if (mmc_fd >= 0)
        {
            char buf[IMG_DISK_SECTION_SIZE];
            for (int i = 0; i < sizeof(buf); i++)
            {
                buf[i] = 0;
            }
            for (DWORD i = 0; i < IMG_DISK_SECTION_COUNT; i++)
            {
                int write_len = write(mmc_fd, buf, IMG_DISK_SECTION_SIZE);
                if (write_len != IMG_DISK_SECTION_SIZE)
                {
                    ret = RES_ERROR;
                    break;
                }
            }
        }
        else
        {
            return RES_NOTRDY;
        }
        close(mmc_fd);
    }
    return ret;
}

static DRESULT _fatfs_disk_read(BYTE *buff, LBA_t sector, UINT count)
{
    DRESULT ret = RES_ERROR;
    int mmc_fd = open(IMG_DISK_FILE, O_RDWR);
    if (mmc_fd >= 0)
    {
        lseek(mmc_fd, sector * IMG_DISK_SECTION_SIZE, L_SET);
        int read_len = read(mmc_fd, buff, count * IMG_DISK_SECTION_SIZE);
        if (read_len == count * IMG_DISK_SECTION_SIZE)
        {
            ret = RES_OK;
        }
    }
    close(mmc_fd);
    return ret;
}

static DRESULT _fatfs_disk_write(const BYTE *buff, LBA_t sector, UINT count)
{
    DRESULT ret = RES_ERROR;
    int mmc_fd = open(IMG_DISK_FILE, O_RDWR);
    if (mmc_fd >= 0)
    {
        lseek(mmc_fd, sector * IMG_DISK_SECTION_SIZE, L_SET);
        int write_len = write(mmc_fd, buff, count * IMG_DISK_SECTION_SIZE);
        if (write_len == count * IMG_DISK_SECTION_SIZE)
        {
            ret = RES_OK;
        }
    }
    close(mmc_fd);
    return ret;
}

static DRESULT _fatfs_disk_ioctl(BYTE cmd, void *buff)
{
    switch (cmd)
    {
    case CTRL_SYNC:
        break;
    case GET_SECTOR_COUNT:
        *(WORD *)buff = IMG_DISK_SECTION_COUNT;
        break;
    case GET_SECTOR_SIZE:
        *(WORD *)buff = IMG_DISK_SECTION_SIZE;
        break;
    case GET_BLOCK_SIZE:
        *(WORD *)buff = 1;
        break;
    case CTRL_TRIM:
        break;
    }
    return RES_OK;
}

static int _init_fatfs_img(void)
{
    device_disk_fatfs_t dev = &s_device;
    if (f_disk_regist(dev, "img", -1) == 0)
    {
        /* 挂载 */
        FRESULT res = f_mount(&dev->data->fs, dev->data->volume_str, 1);
        if (res == FR_NO_FILESYSTEM)
        {
            BYTE work[FF_MAX_SS];
            res = f_mkfs(dev->data->volume_str, 0, work, sizeof(work));
            res = f_mount(NULL, dev->data->volume_str, 0);           // 取消文件系统
            res = f_mount(&dev->data->fs, dev->data->volume_str, 1); // 挂载文件系统
        }
        if (res != FR_OK)
        {
            f_disk_unregist("img");
            SYS_LOG_WRN("%s fs init ERROR!\r\n", dev->data->volume_str);
            return -1;
        }
        return 0;
    }

    SYS_LOG_WRN("%s disk regist ERROR!\r\n", dev->data->volume_str);
    return -1;
}
INIT_EXPORT_DEVICE(_init_fatfs_img);
